<?php
namespace core\models;
/**
 * 网络常用功能
 * @package core\models
 * @author  ZhouHaoHua <53027327@qq.com>
 */
class Network {

    /**
     * 取得客户端IP
     * @return string
     */
    public function getIp() {
        $ip_address   = null;
        $ip_addresses = array();
        if (isset($_SERVER['HTTP_X_FORWARDED_FOR']) && !empty($_SERVER['HTTP_X_FORWARDED_FOR'])) {
            foreach (array_reverse(explode(',', $_SERVER['HTTP_X_FORWARDED_FOR'])) as $x_ip) {
                $x_ip = trim($x_ip);

                if ($this->checkIpAddress($x_ip)) {
                    $ip_addresses[] = $x_ip;
                }
            }
        }
        if (isset($_SERVER['HTTP_CLIENT_IP']) && !empty($_SERVER['HTTP_CLIENT_IP'])) {
            $ip_addresses[] = $_SERVER['HTTP_CLIENT_IP'];
        }
        if (isset($_SERVER['HTTP_X_CLUSTER_CLIENT_IP']) && !empty($_SERVER['HTTP_X_CLUSTER_CLIENT_IP'])) {
            $ip_addresses[] = $_SERVER['HTTP_X_CLUSTER_CLIENT_IP'];
        }
        if (isset($_SERVER['HTTP_PROXY_USER']) && !empty($_SERVER['HTTP_PROXY_USER'])) {
            $ip_addresses[] = $_SERVER['HTTP_PROXY_USER'];
        }
        $ip_addresses[] = $_SERVER['REMOTE_ADDR'];
        foreach ($ip_addresses as $ip) {
            if (!empty($ip) && $this->checkIpAddress($ip)) {
                $ip_address = $ip;
                break;
            }
        }
        return $ip_address;
    }

    /**
     * 根据域名取得该域名的所有IP地址
     * @param string $s 网站主机名如：www.baidu.com，如不传则返回当前网站服务器的IP地址
     * @return array 返回IP地址数组
     */
    public function getServerIps($s = '') {
        $s = $s ? $s : $_SERVER['SERVER_NAME'];
        return gethostbynamel($s);
    }

    /**
     * 根据域名取得该域名的IP地址
     * @param string $s 网站主机名如：www.baidu.com，如不传则返回当前网站服务器的IP地址
     * @return string 返回IP地址
     */
    public static function getServerIp($s = '') {
        $s = $s ? $s : $_SERVER['SERVER_NAME'];
        return gethostbyname($s);
    }

    /**
     * 取得当前服务器IP
     * @return string
     * @author ZhouHaoHua <53027327@qq.com>
     */
    public static function getThisServerIp() {
        return $_SERVER['SERVER_ADDR'];
    }

    /**
     * 验证IP地址(IPv4)是否有效
     * @param string $ip_address 被检查的IP地址
     * @return bool|mixed
     */
    public function checkIpAddress($ip_address) {
        if (function_exists('filter_var') && defined('FILTER_VALIDATE_IP')) {
            return filter_var($ip_address, FILTER_VALIDATE_IP, array('flags' => FILTER_FLAG_IPV4));
        }

        if (preg_match('/^(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})$/', $ip_address)) {
            $parts = explode('.', $ip_address);

            foreach ($parts as $ip_parts) {
                if ((intval($ip_parts) > 255) || (intval($ip_parts) < 0)) {
                    return false; // number is not within 0-255
                }
            }

            return true;
        }

        return false;
    }

    /**
     * 判断当前请求是否是ajax请求
     * @return bool 返回true代表是
     * @author ZhouHaoHua <53027327@qq.com>
     */
    public function isAjax() {
        //[X-Requested-With] => XMLHttpRequest
        $d = $this->getAllHeaders();
        if (isset($d['X-Requested-With']) && $d['X-Requested-With'] == 'XMLHttpRequest') {
            return true;
        }
        return false;
    }

    /**
     * 获取当前请求的所有请求头信息。
     * @author ZhouHaoHua <53027327@qq.com>
     */
    public function getAllHeaders() {
        $headers = array();
        if (!function_exists('getallheaders')) {    //nginx
            foreach ($_SERVER as $name => $value) {
                if (substr($name, 0, 5) == 'HTTP_') {
                    $headers[str_replace(' ', '-', ucwords(strtolower(str_replace('_', ' ', substr($name, 5)))))] = $value;
                }
            }
        } else {
            $headers = getallheaders(); //apache
        }
        return $headers;
    }

    public function Rpc_Server() {
        return new \Rpc_Server();
    }

    public function Rpc_Client() {
        return new \Rpc_Client();
    }

    /**
     * 非阻塞式访问某个页面。
     * 应用场景：如访问一个smtp发邮件的服务页面，不用等待即可执行此后面的代码，大大节约程序时间！
     * 注意：以下代码有\r\n的地方可能在不同服务器有偏差，如果有问题就检查\r\n即可。有些是\r\n，有些需要\r\n\r\n。
     * @param string $url       目标URL。该目标页面中最好有设置set_time_limit(0)的代码！
     * @param array  $post_data POST数组
     * @param array  $cookie    COOKIE数组
     * @param int    $block     1为阻塞，0为非阻塞
     * @return bool 此动作是单向的访问某个页面，不接收返回的信息，可以大量节约时间。
     * @author ZhouHaoHua <53027327@qq.com>
     */
    public static function triggerRequest($url, $post_data = array(), $cookie = array(), $block = 0) {
        //$block     = 0; //1为阻塞，0为非阻塞
        $d         = true;
        $url_array = parse_url($url); //获取URL信息，以便平凑HTTP HEADER
        $host      = $url_array['host'];
        $port      = isset($url_array['port']) ? $url_array['port'] : 80;
        $errno     = '';
        $errstr    = '';
        $timeout   = 30;
        $path      = $url_array['path'] . "?" . $url_array['query'];
        $param     = $post_data;
        $data      = http_build_query($param);
        // create connect
        $fp = fsockopen($host, $port, $errno, $errstr, $timeout);
        stream_set_blocking($fp, $block);
        if (!$fp) {
            return false;
        }
        // send request
        $out = (!empty($data) ? "POST" : "GET")." ${path} HTTP/1.1\r\n";
        $out .= "Host:${host}\r\n";
        $out .= "Cache-Control: no-cache\r\n";
        if (!empty($cookie)) {
            $_cookie = strval(null);
            foreach ($cookie as $k => $v) {
                $_cookie .= $k . "=" . $v . "; ";
            }
            //$cookie_str = "Cookie: " . base64_encode($_cookie) . "\r\n\r\n";//传递Cookie
            $cookie_str = "Cookie: " . $_cookie . "\r\n";//传递Cookie
            $out .= $cookie_str;
        }
        if(!empty($data)) {
            $out .= "Content-type:application/x-www-form-urlencoded\r\n";
            $out .= "Content-length:" . strlen($data) . "\r\n";
            $out .= "Connection:close\r\n\r\n"; //关闭长连接方式，此处位置不可挪【如没这句程序会因Keep-Alive变得很慢】
            $out .= $data;  //POST数据
        }else{
            $out .= "Connection:close\r\n\r\n";
        }

        fputs($fp, $out);
        // get response
        if ($block > 0) {
            $d = array();
            while ($row = fread($fp, 4096)) {
                $d[] = $row;
            }
        }
        fclose($fp);
        return $d;
    }

    /**
     * PHP-HTTP断点续传
     * @param string $path: 文件所在路径
     * @param string $file: 文件名
     * @return void
     */
    public static function rangeDownload($path,$file) {
        $real = str_replace('//','/',$path.'/'.$file);
        if(!file_exists($real)) {
            return false;
        }
        $size = filesize($real);
        $size2 = $size-1;
        $range = 0;
        if(isset($_SERVER['HTTP_RANGE'])) {   //http_range表示请求一个实体/文件的一个部分,用这个实现多线程下载和断点续传！
            header('HTTP /1.1 206 Partial Content');
            $range = str_replace('=','-',$_SERVER['HTTP_RANGE']);
            $range = explode('-',$range);
            $range = trim($range[1]);
            header('Content-Length:'.$size);
            header('Content-Range: bytes '.$range.'-'.$size2.'/'.$size);
        } else {
            header('Content-Length:'.$size);
            header('Content-Range: bytes 0-'.$size2.'/'.$size);
        }
        header('Accenpt-Ranges: bytes');
        header('Content-Type: application/octet-stream');
        header("Cache-control: public");
        header("Pragma: public");
        //解决在IE中下载时中文乱码问题
        $ua = $_SERVER['HTTP_USER_AGENT'];
        if(preg_match('/MSIE/',$ua)) {    //表示正在使用 Internet Explorer。
            $ie_filename = str_replace('+','%20',urlencode($file));
            header('Content-Dispositon:attachment; filename='.$ie_filename);
        } else {
            header('Content-Dispositon:attachment; filename='.$file);
        }
        $fp = fopen($real,'rb+');
        fseek($fp,$range);                //fseek:在打开的文件中定位,该函数把文件指针从当前位置向前或向后移动到新的位置，新位置从文件头开始以字节数度量。成功则返回 0；否则返回 -1。注意，移动到 EOF 之后的位置不会产生错误。
        while(!feof($fp)) {               //feof:检测是否已到达文件末尾 (eof)
            set_time_limit(0);            //注释①
            print(fread($fp,1024));       //读取文件（可安全用于二进制文件,第二个参数:规定要读取的最大字节数）
            ob_flush();                   //刷新PHP自身的缓冲区
            flush();                      //刷新缓冲区的内容(严格来讲, 这个只有在PHP做为apache的Module(handler或者filter)安装的时候, 才有实际作用. 它是刷新WebServer(可以认为特指apache)的缓冲区.)
        }
        fclose($fp);
    }
}